package com.mybatis_demo.frame.reflection;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author huyoufu
 * @TIME 2019/1/11 7:30
 * @description
 */
public class ReflectorUtil {
    private static Map<Class,BeanMethodInfo> cache=new HashMap<>();

    
    private static BeanMethodInfo getBeanMethodInfo(Object o){
        Class<?> oClass = o.getClass();
        BeanMethodInfo beanMethodInfo = cache.get(oClass);
        if (beanMethodInfo==null){
            beanMethodInfo = new BeanMethodInfo(o.getClass());
            cache.put(oClass,beanMethodInfo);
        }
        return beanMethodInfo;

    }
    public static void set(Object o,String propertyName,Object propertyValue){
        BeanMethodInfo beanMethodInfo = getBeanMethodInfo(o);
        Method method = beanMethodInfo.setterMethod(propertyName);
        try {
            method.invoke(o,propertyValue);
        } catch (Exception e) {
            throw new RuntimeException("set--"+propertyName+"--property failed!",e);
        }
    }
    public static Object get(Object o,String propertyName){
        BeanMethodInfo beanMethodInfo = getBeanMethodInfo(o);
        Method method = beanMethodInfo.getterMethod(propertyName);
        try {
            return method.invoke(o);
        } catch (Exception e) {
            throw new RuntimeException("get--"+propertyName+"--property failed!",e);
        }
    }

    private static final class BeanMethodInfo{

        private Class clazz;
        private Map<String,Method> setterMethods=new HashMap<>();
        private Map<String,Method> getterMethods=new HashMap<>();

        public BeanMethodInfo(Class clazz) {
            this.clazz = clazz;
            addGetterMethods();
            addSetterMethods();
        }


        public Method setterMethod(String property){
            return setterMethods.get(property);
        }
        public Method getterMethod(String property){
            return getterMethods.get(property);
        }
        private  String methodToProperty(String name) {
            if(name.startsWith("is")) {
                name = name.substring(2);
            } else {
                if(!name.startsWith("get") && !name.startsWith("set")) {
                    throw new RuntimeException("Error parsing property name \'" + name + "\'.  Didn\'t start with \'is\', \'get\' or \'set\'.");
                }

                name = name.substring(3);
            }

            if(name.length() == 1 || name.length() > 1 && !Character.isUpperCase(name.charAt(1))) {
                name = name.substring(0, 1).toLowerCase() + name.substring(1);
            }

            return name;
        }
        private void addSetterMethods(){
            Method[] methods = clazz.getMethods();
            int len = methods.length;

            for(int i = 0; i < len; i++) {
                Method method = methods[i];
                String name = method.getName();
                if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) {
                    String property = methodToProperty(name);
                    setterMethods.put(property,method);
                }
            }

        }
        private void addGetterMethods(){
            Method[] methods = clazz.getMethods();
            int len = methods.length;

            for(int i = 0; i < len; i++) {
                Method method = methods[i];
                String name = method.getName();
                if(name.startsWith("get") && name.length() > 3 || name.startsWith("is") && name.length() > 2) {
                    String property = methodToProperty(name);
                    getterMethods.put(property,method);
                }
            }

        }
    }
}
